
#include <BOpenGL>
#include <BUniform>
#include "member_BUniform.h"
#include "member_BProgram.h"

using namespace BWE;

#define member					(*(member_BUniform*)_ptr)
#define member_allocate()		_ptr = new member_BUniform(this)
#define member_release()		delete (member_BUniform*)_ptr

BUniform::BUniform()
{
	member_allocate();
}
BUniform::BUniform(const BString& name, Type type, int size) : BObject(name)
{
	member_allocate();
	member.init(type, size);
}
BUniform::BUniform(const BString& name, int value, int size) : BObject(name)
{
	member_allocate();
	member.init(Type_Int, size);
	int* data = (int*)member.value.data();
	for (int i = 0; i < size; i++)
	{
		data[i] = value;
	}
	member.dirty = true;
}
BUniform::BUniform(const BString& name, float value, int size) : BObject(name)
{
	member_allocate();
	member.init(Type_Float, size);
	float* data = (float*)member.value.data();
	for (int i = 0; i < size; i++)
	{
		data[i] = value;
	}
	member.dirty = true;
}
BUniform::BUniform(const BString& name, const BVector& vec, int size) : BObject(name)
{
	member_allocate();
	member.init(Type_Vec3f, size);
	BVector* data = (BVector*)member.value.data();
	for (int i = 0; i < size; i++)
	{
		data[i] = vec;
	}
	member.dirty = true;

}
BUniform::BUniform(const BString& name, const BColorf& color, int size) : BObject(name)
{
	member_allocate();
	member.init(Type_Vec4f, size);
	BColorf* data = (BColorf*)member.value.data();
	for (int i = 0; i < size; i++)
	{
		data[i] = color;
	}
	member.dirty = true;
}
BUniform::BUniform(const BString& name, const BMatrix& matrix, int size) : BObject(name)
{
	member_allocate();
	member.init(Type_Matrixf, size);
	BMatrix* data = (BMatrix*)member.value.data();
	for (int i = 0; i < size; i++)
	{
		data[i] = matrix;
	}
	member.dirty = true;
}
BUniform::~BUniform()
{
	member_release();
}

bool BUniform::setName(const BString& name)
{
	if (BObject::setName(name))
	{
		member.dirty = true;
		return true;
	}
	return false;
}

void BUniform::setType(Type type)
{
	if (member.type != type)
	{
		member.type = type;
		member.dirty = true;
	}
}
BUniform::Type BUniform::type() const
{
	return member.type;
}

void BUniform::setSize(int size)
{
	if (member.size != size)
	{
		member.size = size;
		member.dirty = true;
	}
}
int BUniform::size() const
{
	return member.size;
}

bool BUniform::set(int value)
{
	if (member.type == Type_Int)
	{
		int* data = (int*)member.value.data();
		*data = value;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(BUInt value)
{
	if (member.type == Type_UInt)
	{
		BUInt* data = (BUInt*)member.value.data();
		*data = value;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(float value)
{
	if (member.type == Type_Float)
	{
		float* data = (float*)member.value.data();
		*data = value;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(double value)
{
	if (member.type == Type_Double)
	{
		double* data = (double*)member.value.data();
		*data = value;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(const BPointf& vec2)
{
	if (member.type == Type_Vec2f)
	{
		BPointf* data = (BPointf*)member.value.data();
		*data = vec2;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(const BPointd& vec2)
{
	if (member.type == Type_Vec2d)
	{
		BPointd* data = (BPointd*)member.value.data();
		*data = vec2;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(const BVectorf& vec3)
{
	if (member.type == Type_Vec3f)
	{
		BVectorf* data = (BVectorf*)member.value.data();
		*data = vec3;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(const BVectord& vec3)
{
	if (member.type == Type_Vec3d)
	{
		BVectord* data = (BVectord*)member.value.data();
		*data = vec3;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(const BColorf& color)
{
	if (member.type == Type_Vec4f)
	{
		BColorf* data = (BColorf*)member.value.data();
		*data = color;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(const BMatrix& matrix)
{
	if (member.type == Type_Matrixf)
	{
		BMatrix* data = (BMatrix*)member.value.data();
		*data = matrix;
		member.dirty = true;
		return true;
	}
	return false;
}

bool BUniform::set(int pos, int value)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Int)
	{
		int* data = (int*)member.value.data();
		data[pos] = value;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(int pos, BUInt value)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_UInt)
	{
		BUInt* data = (BUInt*)member.value.data();
		data[pos] = value;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(int pos, float value)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Float)
	{
		float* data = (float*)member.value.data();
		data[pos] = value;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(int pos, double value)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Double)
	{
		double* data = (double*)member.value.data();
		data[pos] = value;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(int pos, const BPointf& vec2)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Vec2f)
	{
		BPointf* data = (BPointf*)member.value.data();
		data[pos] = vec2;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(int pos, const BPointd& vec2)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Vec2d)
	{
		BPointd* data = (BPointd*)member.value.data();
		data[pos] = vec2;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(int pos, const BVectorf& vec3)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Vec3f)
	{
		BVectorf* data = (BVectorf*)member.value.data();
		data[pos] = vec3;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(int pos, const BVectord& vec3)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Vec3d)
	{
		BVectord* data = (BVectord*)member.value.data();
		data[pos] = vec3;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(int pos, const BColorf& color)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Vec4f)
	{
		BColorf* data = (BColorf*)member.value.data();
		data[pos] = color;
		member.dirty = true;
		return true;
	}
	return false;
}
bool BUniform::set(int pos, const BMatrix& matrix)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Matrixf)
	{
		BMatrix* data = (BMatrix*)member.value.data();
		data[pos] = matrix;
		member.dirty = true;
		return true;
	}
	return false;
}

bool BUniform::get(int& value)
{
	if (member.type == Type_Int)
	{
		int* data = (int*)member.value.data();
		value = *data;
		return true;
	}
	return false;
}
bool BUniform::get(BUInt& value)
{
	if (member.type == Type_UInt)
	{
		BUInt* data = (BUInt*)member.value.data();
		value = *data;
		return true;
	}
	return false;
}
bool BUniform::get(float& value)
{
	if (member.type == Type_Float)
	{
		float* data = (float*)member.value.data();
		value = *data;
		return true;
	}
	return false;
}
bool BUniform::get(double& value)
{
	if (member.type == Type_Double)
	{
		double* data = (double*)member.value.data();
		value = *data;
		return true;
	}
	return false;
}
bool BUniform::get(BPointf& vec2)
{
	if (member.type == Type_Vec2f)
	{
		BPointf* data = (BPointf*)member.value.data();
		vec2 = *data;
		return true;
	}
	return false;
}
bool BUniform::get(BPointd& vec2)
{
	if (member.type == Type_Vec2d)
	{
		BPointd* data = (BPointd*)member.value.data();
		vec2 = *data;
		return true;
	}
	return false;
}
bool BUniform::get(BVectorf& vec3)
{
	if (member.type == Type_Vec3f)
	{
		BVectorf* data = (BVectorf*)member.value.data();
		vec3 = *data;
		return true;
	}
	return false;
}
bool BUniform::get(BVectord& vec3)
{
	if (member.type == Type_Vec3d)
	{
		BVectord* data = (BVectord*)member.value.data();
		vec3 = *data;
		return true;
	}
	return false;
}
bool BUniform::get(BColorf& color)
{
	if (member.type == Type_Vec4f)
	{
		BColorf* data = (BColorf*)member.value.data();
		color = *data;
		return true;
	}
	return false;
}
bool BUniform::get(BMatrix& matrix)
{
	if (member.type == Type_Matrixf)
	{
		BMatrix* data = (BMatrix*)member.value.data();
		matrix = *data;
		member.dirty = true;
		return true;
	}
	return false;
}

bool BUniform::get(int pos, int& value)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Int)
	{
		int* data = (int*)member.value.data();
		value = data[pos];
		return true;
	}
	return false;
}
bool BUniform::get(int pos, BUInt& value)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_UInt)
	{
		BUInt* data = (BUInt*)member.value.data();
		value = data[pos];
		return true;
	}
	return false;
}
bool BUniform::get(int pos, float& value)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Float)
	{
		float* data = (float*)member.value.data();
		value = data[pos];
		return true;
	}
	return false;
}
bool BUniform::get(int pos, double& value)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Double)
	{
		double* data = (double*)member.value.data();
		value = data[pos];
		return true;
	}
	return false;
}
bool BUniform::get(int pos, BPointf& vec2)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Vec2f)
	{
		BPointf* data = (BPointf*)member.value.data();
		vec2 = data[pos];
		return true;
	}
	return false;
}
bool BUniform::get(int pos, BPointd& vec2)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Vec2d)
	{
		BPointd* data = (BPointd*)member.value.data();
		vec2 = data[pos];
		return true;
	}
	return false;
}
bool BUniform::get(int pos, BVectorf& vec3)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Vec3f)
	{
		BVectorf* data = (BVectorf*)member.value.data();
		vec3 = data[pos];
		return true;
	}
	return false;
}
bool BUniform::get(int pos, BVector& vec3)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Vec3d)
	{
		BVectord* data = (BVectord*)member.value.data();
		vec3 = data[pos];
		return true;
	}
	return false;
}
bool BUniform::get(int pos, BColorf& color)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Vec4f)
	{
		BColorf* data = (BColorf*)member.value.data();
		color = data[pos];
		return true;
	}
	return false;
}
bool BUniform::get(int pos, BMatrix& matrix)
{
	if (pos < 0 || pos >= member.size)
		return false;
	if (member.type == Type_Matrixf)
	{
		BMatrix* data = (BMatrix*)member.value.data();
		matrix = data[pos];
		return true;
	}
	return false;
}

bool BUniform::flush() const
{
	if (member.dirty && member.location >= 0)
	{
		switch (member.type)
		{
		case BUniform::Type_Int:
		{
			int* data = (int*)member.value.data();
			glUniform1iv(member.location, member.size, (GLint*)data);
			break;
		}
		case BUniform::Type_UInt:
		{
			unsigned int* data = (unsigned int*)member.value.data();
			glUniform1uiv(member.location, member.size, (GLuint*)data);
			break;
		}
		case BUniform::Type_Float:
		{
			float* data = (float*)member.value.data();
			glUniform1fv(member.location, member.size, (GLfloat*)data);
			break;
		}
		case BUniform::Type_Double:
		{
			double* data = (double*)member.value.data();
			glUniform1dv(member.location, member.size, (GLdouble*)data);
			break;
		}
		case BUniform::Type_Vec2f:
		{
			BPointf* data = (BPointf*)member.value.data();
			glUniform2fv(member.location, member.size, (GLfloat*)data);
			break;
		}
		case BUniform::Type_Vec2d:
		{
			BPointd* data = (BPointd*)member.value.data();
			glUniform2dv(member.location, member.size, (GLdouble*)data);
			break;
		}
		case BUniform::Type_Vec3f:
		{
			BVectorf* data = (BVectorf*)member.value.data();
			glUniform3fv(member.location, member.size, (GLfloat*)data);
			break;
		}
		case BUniform::Type_Vec3d:
		{
			BVectord* data = (BVectord*)member.value.data();
			glUniform3dv(member.location, member.size, (GLdouble*)data);
			break;
		}
		case BUniform::Type_Vec4f:
		{
			BColorf* data = (BColorf*)member.value.data();
			glUniform4fv(member.location, member.size, (GLfloat*)data);
			break;
		}
		case BUniform::Type_Vec4d:
		{
			double* data = (double*)member.value.data();
			glUniform4dv(member.location, member.size, (GLdouble*)data);
			break;
		}
		case BUniform::Type_Matrixf:
			break;
		case BUniform::Type_Matrixd:
			break;
		default:
			break;
		}
		member.dirty = false;
	}
	return false;
}
